package com.magenta.mrs.basegeocoding;

import com.magenta.maxoptra.gis.*;
import com.magenta.maxoptra.gis.commons.Logger;
import com.magenta.mrs.util.GeoInformationClient;
import org.apache.commons.lang.BooleanUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.magenta.maxoptra.gis.GisParameter.*;

/**
 * Project: MaxOptra
 * User: kaganov
 * Date: 26.11.2008 15:27:27
 * <p/>
 * Copyright (c) 1999-2007 Magenta Corporation Ltd. All Rights Reserved.
 * Magenta Technology proprietary and confidential.
 * Use is subject to license terms.
 */
public abstract class BaseGeocoding<T> implements IGeoConfigurable, IGeocoding {

    protected GeoSettings geoSettings;

    protected Logger logger = new Logger(getClass());

    protected GeoInformationClient client;

    protected static final String GETTING_CONNECTION_ACTION = "getting connection";

    @Override
    public String getVersion() {
        return null;
    }

    @Override
    public String getClassName() {
        return this.getClass().getName();
    }

    public Map<String, GeoStatusCode> getProviderErrorMap() {
        return new HashMap<String, GeoStatusCode>();
    }

    public Boolean getGisBooleanProperty(GisParameter paramater) {
        GisParameters properties = client.getProperties();
        String providerName = getProviderName();
        String property = properties.get(paramater.withProvider(providerName));
        return property == null || Boolean.parseBoolean(property);
    }

    public String getGisStringProperty(GisParameter parameter) {
        GisParameters properties = client.getProperties();
        String providerName = getProviderName();
        return properties.get(parameter.withProvider(providerName));
    }

    public Integer getGisIntegerProperty(GisParameter parameter) {
        GisParameters properties = client.getProperties();
        String providerName = getProviderName();
        String property = properties.get(parameter.withProvider(providerName));
        return property == null ? null : Integer.parseInt(property);
    }

    public Float getGisFloatProperty(GisParameter parameter) {
        GisParameters properties = client.getProperties();
        String providerName = getProviderName();
        String property = properties.get(parameter.withProvider(providerName));
        return property == null ? null : Float.parseFloat(property);
    }

    protected String formatString(String text){
        return text.replaceAll(", ", "% ").replaceAll(" ", "+");
    }

    protected boolean isLoggingAllowed() {
        boolean allowed = false;
        if (client != null) {
            String enableLoggingStr = client.getProperties().get(enableLogging.withProvider(getProviderName()));
            allowed = BooleanUtils.toBoolean(enableLoggingStr);
        }
        return allowed;
    }

    protected VehicleType getDefaultVehicleType() {
        String vehicleType = getGisStringProperty(default_vehicle_type);
        if (vehicleType != null) {
            return VehicleType.valueOf(vehicleType);
        }
        return VehicleType.Default;
    }

    protected String getAccountGlobalId() {
        String globalId = null;
        if (client != null) {
            globalId = client.getProperties().get(accountName.name());
        }
        return globalId;
    }

    public List<GeoAddress> findAddressByKeyword(GeoProviderSettings settings, String keyword, int i) throws GeoException {
        return doFindAddressByKeyword(settings, keyword, i, null);
    }

    protected GisParameters getProviderParams() {
        return client.getProperties();
    }

    public void setClient(GeoInformationClient client) {
        this.client = client;
    }

    public GeoInformationClient getClient() {
        return client;
    }

    public GeoAddress findBestAddressByKeyword(GeoProviderSettings settings, String keyword) throws GeoException {
        GisParameters gisParams = getProviderParams();
        if (gisParams != null) {
            String providertype = (String) gisParams.get(provider_type.name());
            if (providertype != null) {
                String providerName = getProviderName();
                String appAccuracy = (String) gisParams.get(geocoding_accuracy.withProvider(providerName));
                if (appAccuracy != null) { // Assumption: search results are sorted by accuracy
                    List<T> accuracies = new ArrayList<T>();
                    List<GeoAddress> addressList = doFindAddressByKeyword(settings, keyword, 1, accuracies);
                    if (addressList.size() > 0) {
                        boolean allowedAccuracy = accuracyIsAccurateEnough(appAccuracy, accuracies.get(0));
                        if (allowedAccuracy) {
                            return addressList.get(0);
                        }
                    }
                } else {
                    logger.warn("accuracyThreshold is not set for provider " + providertype);
                }
            } else {
                logger.warn("gis providertype is not set");
            }
        }
        return null;
    }

    protected abstract boolean accuracyIsAccurateEnough(String accuracyThreshold, T accuracy);

    protected abstract List<GeoAddress> doFindAddressByKeyword(GeoProviderSettings settings, String keyword, int i, List<T> accuracies) throws GeoException;

    private Long cid;

    public Long classId() {
        if (cid == null) {
            cid = ((long) getClass().getSimpleName().hashCode()) |
                    ((long) getClass().getPackage().getName().hashCode()) << 32;
        }
        return cid;
    }


}
